package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.model.app.mess.AggBehaviorDTO;
import com.heima.model.app.mess.NewBehaviorDTO;
import com.heima.model.common.constants.article.HotArticleConstants;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author mrchen
 * @date 2022/1/11 11:19
 */
@Component
@Slf4j
public class UpdateHotArticleJob {
    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    HotArticleService hotArticleService;

    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String param){
        // 1. 取出redis中 最近10s中所产生 最新的行为数据   [   articleId : 1   , like 7  view 10  comment 11]
        List<NewBehaviorDTO> behaviorList = getRedisBehaviorList();
        if(CollectionUtils.isEmpty(behaviorList)){
            log.info(" 太冷清了，我们项目的文章 没有任何人访问~ ");
            return ReturnT.SUCCESS;
        }
        // 2. 将最新的行为数据进行聚合处理  每篇文章 封装成一条数据
        List<AggBehaviorDTO>  aggBehaviorDTOList =  getAggBehaviorList(behaviorList);
        if(CollectionUtils.isEmpty(aggBehaviorDTOList)){
            log.info(" 太冷清了，我们项目的文章 没有任何人访问~ ");
            return ReturnT.SUCCESS;
        }
        // 3. 根据行为聚合数据  修改文章的热度值
        aggBehaviorDTOList.forEach(hotArticleService::updateApArticle);
        return ReturnT.SUCCESS;
    }

    /**
     * 按照文章id分组  并统计每篇文章的聚合行为结果
     * NewBehavior ==> AggBehavior
     * @param behaviorList
     * @return
     */
    private List<AggBehaviorDTO> getAggBehaviorList(List<NewBehaviorDTO> behaviorList) {
        // 1. 按照文章id对行为进行分组
        List<AggBehaviorDTO> aggBehaviorDTOList = new ArrayList<>();
        Map<Long, List<NewBehaviorDTO>> behaviorGroupByArticleId =
                behaviorList.stream().collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));
        // 2. 计算每个分组对应的聚合行为数据
        behaviorGroupByArticleId.forEach((articleId,behaviorDTOList)->{
            // 将每一个behaviorList集合  封装 成一个对象  AggBehavior
            //  reduce                                                             {articleId:11 , likes: 1,views:1,c:1,comment:0}
            //                      {articleId:11 , likes: 1,views:1,c:0,comment:0}
            // {articleId:11 , likes: 0,views:1,c:0,comment:0}  {articleId:11 , likes: 1,views:0,c:0,comment:0} {articleId:11 , likes: 0,views:0,c:1,comment:0}
            // {add: 1,articleId: 11 , type: VIEWS}  {add: 1,articleId: 11 , type: LIKES} {add: 1,articleId: 11 , type: COLLECTION}
            Optional<AggBehaviorDTO> reduceResult = behaviorDTOList.stream().map(behavior -> {
                AggBehaviorDTO aggBehaviorDTO = new AggBehaviorDTO();
                aggBehaviorDTO.setArticleId(articleId);
                switch (behavior.getType()) {
                    case LIKES:
                        aggBehaviorDTO.setLike(behavior.getAdd());
                        break;
                    case VIEWS:
                        aggBehaviorDTO.setView(behavior.getAdd());
                        break;
                    case COLLECTION:
                        aggBehaviorDTO.setCollect(behavior.getAdd());
                        break;
                    case COMMENT:
                        aggBehaviorDTO.setComment(behavior.getAdd());
                        break;
                    default:

                }
                return aggBehaviorDTO;
            }).reduce((agg1, agg2) -> {
                agg1.setLike(agg1.getLike() + agg2.getLike());
                agg1.setView(agg1.getView() + agg2.getView());
                agg1.setCollect(agg1.getCollect() + agg2.getCollect());
                agg1.setComment(agg1.getComment() + agg2.getComment());
                return agg1;
            });
            // 判断是否包含聚合结果
            if (reduceResult.isPresent()) {
                aggBehaviorDTOList.add(reduceResult.get());
            }
        });
        return aggBehaviorDTOList;
    }
    /**
     * 获取最近redis中 新增的 行为数据
     * @return
     */
    private List<NewBehaviorDTO> getRedisBehaviorList() {
        // 1.1 获取redis list中数据的长度 size
        Long size = redisTemplate.opsForList().size(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);
        //    开启redis的管道命令
        List<Object> pipelinedResult = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                // 开启管道执行命令
                connection.openPipeline();
                // 1.2  通过lrange方法 (0, size - 1)  获取redis列表数据
                connection.lRange(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), 0, size - 1);
                // 1.3  通过ltrim方法 (size, - 1)  删除获取的redis列表数据
                connection.lTrim(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), size, -1);
                return null;
            }
        }, RedisSerializer.string());
        //    统一获取管道命令  结果    List
        if(CollectionUtils.isNotEmpty(pipelinedResult)){
            List<String> list = (List<String>)pipelinedResult.get(0);
            return list.stream().map(str -> JSON.parseObject(str, NewBehaviorDTO.class)).collect(Collectors.toList());
        }
        return null;
    }
}